/*
我们提供一个类：

class FooBar {
  public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
    }
  }

  public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
  }
}
两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法，另一个线程将会调用 bar() 方法。

请设计修改程序，以确保 "foobar" 被输出 n 次。

 

示例 1:

输入: n = 1
输出: "foobar"
解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法，"foobar" 将被输出一次。
示例 2:

输入: n = 2
输出: "foobarfoobar"
解释: "foobar" 将被输出两次。

来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/print-foobar-alternately
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
*/

#include "../stdc++.h"

// 创建两个mutex类型变量，利用其lock和unlock函数实现（未遵循only the owner can unlock the mutex）
class FooBar {
private:
    int n;
    mutex m1;
    mutex m2;

public:
    FooBar(int n) {
        this->n = n;
        m2.lock();
    }

    void foo(function<void()> printFoo) {
        for (int i = 0; i < n; i++) {
            m1.lock();
        	// printFoo() outputs "foo". Do not change or remove this line.
        	printFoo();
            m2.unlock();
        }
    }

    void bar(function<void()> printBar) {
        for (int i = 0; i < n; i++) {
            m2.lock();
        	// printBar() outputs "bar". Do not change or remove this line.
        	printBar();
            m1.unlock();
        }
    }
};

// 使用atomic模板进行实现
class FooBar {
private:
    int n;
    atomic<bool> fooed{false};

public:
    FooBar(int n) {
        this->n = n;
    }

    void foo(function<void()> printFoo) {
        
        for (int i = 0; i < n; i++) {
            while (fooed.load()) {
                this_thread::yield(); // 交出时间片
            }
        	// printFoo() outputs "foo". Do not change or remove this line.
        	printFoo();
            fooed.store(true);
        }
    }

    void bar(function<void()> printBar) {
        
        for (int i = 0; i < n; i++) {
            while (!fooed.load()) {
                this_thread::yield();
            }
        	// printBar() outputs "bar". Do not change or remove this line.
        	printBar();
            fooed.store(false);
        }
    }
};

// 通过mutex和condition_variable来实现
class FooBar {
private:
    int n;
    int count{1}; // 条件变量
    std::condition_variable v; // 条件变量对象
    std::mutex mu;

public:
    FooBar(int n) {
        this->n = n;
    }

    void foo(function<void()> printFoo) {
        
        for (int i = 0; i < n; i++) {
            std::unique_lock<std::mutex> ul(mu);
            v.wait(ul, [this](){return count == 1;}); // 看看v是否满足条件，锁和变量
        	// printFoo() outputs "foo". Do not change or remove this line.
        	printFoo();
            ++count; // 变量+1，=2，等于2时，foo会阻塞，bar的condition_variable 满足条件
            v.notify_one(); // 通知并唤醒阻塞在v2里面的线程
        }
    }

    void bar(function<void()> printBar) {
        
        for (int i = 0; i < n; i++) {
            std::unique_lock<std::mutex> ul(mu);
            v.wait(ul, [this](){return count == 2;});
        	// printBar() outputs "bar". Do not change or remove this line.
        	printBar();
            --count;
            v.notify_one();
        }
    }
};


#include <semaphore.h>
// 信号量
class FooBar {
private:
    int n;
    sem_t foo_done;
    sem_t bar_done;

public:
    FooBar(int n) {
        this->n = n;
        sem_init(&foo_done, 0, 0);
        sem_init(&bar_done, 0, 1);
    }

    void foo(function<void()> printFoo) { 
        for (int i = 0; i < n; i++) {
            sem_wait(&bar_done);
        	// printFoo() outputs "foo". Do not change or remove this line.
        	printFoo();
            sem_post(&foo_done);
        }
    }

    void bar(function<void()> printBar) {
        for (int i = 0; i < n; i++) {
            sem_wait(&foo_done);
        	// printBar() outputs "bar". Do not change or remove this line.
        	printBar();
            sem_post(&bar_done);
        }
    }
};